package es.uned.etsii.si3.taller.servlets;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.math.BigDecimal;
import java.util.regex.Pattern;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.Preparable;

import com.j256.ormlite.dao.GenericRawResults;
import com.j256.ormlite.dao.RawRowMapper;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.Where;

import es.uned.etsii.si3.taller.*;
import es.uned.etsii.si3.taller.misc.ProporcionaFuncionalidad;

public final class Talleres extends ActionSupport implements Preparable {
	private static final long serialVersionUID = 1L;

	private List<TipoSeccion> tipoTalleres = null;		// Contiene las distintas secciones de talleres
	private int seleccionTipoTaller = -1;				// Contiene el valor seleccionado de la dropbox
	private TipoSeccion tuTipoTaller;					// Contiene la sección seleccionada
	private TipoSeccion tipoTallerDefecto = null;		// Sección por defecto

	private List<Poblacion> localidades =  null;		// Contiene el listado de poblaciones con taller
	private int seleccionLocalidad = -1;				// Contiene el valor seleccionado de la dropbox
	private Poblacion tuLocalidad;						// Contiene la localidad seleccionada
	private Poblacion localidadDefecto = null;			// Localidad por defecto
	
	private String precioMaximo;						// Contiene el precio máximo por hora del taller

	private List<Taller> listadoTalleres = null;		// Listado obtenido de talleres

	private int tallerSeleccionado = 0;					// id del taller seleccionado para ver su ocupación
	private Taller datosTallerSeleccionado;
	
	private String res;									// Resultado que devuelve la acción
	
	private Map<Integer, String> nombreSecciones;
	private List<Object> ocupacionPorSecciones;
	
	public class OcupacionSeccion {
		private String nombre;
		private int ocupacion;
		
		public OcupacionSeccion(String n, int o) {
			setNombre(n);
			setOcupacion(o);
		}

		public String getNombre() {
			return nombre;
		}

		public void setNombre(String nombre) {
			this.nombre = nombre;
		}

		public int getOcupacion() {
			return ocupacion;
		}

		public void setOcupacion(int ocupacion) {
			this.ocupacion = ocupacion;
		}
		
		public String toString() {
			return this.nombre + ": " + ocupacion;
		}
	}

// Getters and setters	
	
	public List<TipoSeccion> getTipoTalleres() {
		return tipoTalleres;
	}
 
	public void setTipoTalleres(List<TipoSeccion> tipoTalleres) {
		this.tipoTalleres = tipoTalleres;
	}
 
	public void setSeleccionTipoTaller(int tuTipoTaller) {
		this.seleccionTipoTaller = tuTipoTaller;
	}
 
	public TipoSeccion getTipoTallerDefecto() {
		return tipoTallerDefecto;
	}

	public void setSeleccionLocalidad(int tuLocalidad) {
		this.seleccionLocalidad = tuLocalidad;
	}

	public void setTipoTallerDefecto(TipoSeccion tipoTallerDefecto) {
		this.tipoTallerDefecto = tipoTallerDefecto;
	}
	
	public Poblacion getTuLocalidad() {
		return tuLocalidad;
	}
	
	public TipoSeccion getTuTipoTaller() {
		return tuTipoTaller;
	}

	public List<Poblacion> getLocalidades() {
		return localidades;
	}
 
	public void setLocalidades(List<Poblacion> localidades) {
		this.localidades = localidades;
	}

	public Poblacion getLocalidadDefecto() {
		return localidadDefecto;
	}
	
	public void setLocalidadDefecto(Poblacion localidadDefecto) {
		this.localidadDefecto = localidadDefecto;
	}

	public List<Taller> getListadoTalleres() {
		return listadoTalleres;
	}
 
	public void setListadoTalleres(List<Taller> listadoTalleres) {
		this.listadoTalleres = listadoTalleres;
	}

	public String getPrecioMaximo () {
		return precioMaximo;
	}
	
	public void setPrecioMaximo(String precioMaximo) {
		this.precioMaximo = precioMaximo;
	}

	public int getTallerSeleccionado () {
		return tallerSeleccionado;
	}
	
	public void setTallerSeleccionado (int tallerSeleccionado){
		this.tallerSeleccionado = tallerSeleccionado;
	}
	
	public Taller getDatosTallerSeleccionado() {
		return datosTallerSeleccionado;
	}

	public List<Object> getOcupacionPorSecciones() {
		return ocupacionPorSecciones;
	}

// Constructor	
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.USUARIO_BUSCAR_TALLERES)	
	public Talleres(){
		
		super();

// Creamos el listado de secciones añadiendo como primera la sección por defecto "Indiferente"		
		TipoSeccion indSec = new TipoSeccion();
		indSec.setNombre("Indiferente");
		setTipoTallerDefecto(indSec);
		
// Creamos el listado de poblaciones añadiendo como primera la población por defecto "Indiferente"		
		Poblacion indPob = new Poblacion();
		indPob.setNombre("Indiferente");
		setLocalidadDefecto(indPob);

	}

	/**
	 * carga los datos de los desplegables para buscar taller
	 */
	@Override
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.USUARIO_BUSCAR_TALLERES)	
	public void prepare() {
		// TODO Auto-generated method stub
		try {
			List<TipoSeccion> listaTipoSecciones;

			listaTipoSecciones = FactoriaDAO.getDao(TipoSeccion.class).queryBuilder()
					.query();
			listaTipoSecciones.add(0, getTipoTallerDefecto());
			setTipoTalleres(listaTipoSecciones);
		} catch (SQLException e) {
			addActionError("Error al cargar los tipos de secciones: " + e);
		}		

		// Cargamos ahora las localidades con taller

		try {
			List<Poblacion> poblaciones;

			// Crea un QueryBuilder por cada una de las tablas que queremos usar
			QueryBuilder<Taller, Integer> tallerQb = FactoriaDAO.getDao(Taller.class)
					.queryBuilder();
			tallerQb.where().eq(Taller.ACTIVO_NAME, true);
			QueryBuilder<Poblacion, Integer> poblacionQb = 
					FactoriaDAO.getDao(Poblacion.class).queryBuilder();
			poblaciones = poblacionQb.join(tallerQb) // Hace un join sobre poblacion seleccionando por taller.poblacion
					.distinct()    // SELECT DISTINCT
					// Ordena por nombre de poblacion...
					.orderBy(Poblacion.NOMBRE_FIELD_NAME, true)
					// Y ahora es cuando efectuamos la consulta
					.query();
			poblaciones.add(0, getLocalidadDefecto());
			setLocalidades(poblaciones);
		} catch (SQLException e) {
			addActionError("Error al cargar las localidades: " + e);
		}
	}
	
	/** 
	 * Valida que el campo precio sea un valor correcto
	 */
	public void validate() {
		if (precioMaximo != null) {
			precioMaximo = precioMaximo.trim();
		}
		if ((precioMaximo != null ) && (!precioMaximo.isEmpty()) && (!testBigDecimal(precioMaximo))) {
			addFieldError("precioMaximo", "Formato erróneo en el campo precio");
		}
	}

	/** 
	 * Método que genera los valores de las dropbox según los datos existentes en la BD	
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.USUARIO_BUSCAR_TALLERES)	
	public String display() {
		prepare();

		if (hasActionErrors()) { 
			return ERROR; 
		} else { 		
			return NONE; 			
		}
	}


	/** 
	 * Genera los resultados de la búsqueda
	 * return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.USUARIO_BUSCAR_TALLERES)	
	public String execute() {

		res = new String (SUCCESS);		

		try {
			if (seleccionTipoTaller == getTipoTallerDefecto().getId()) {
				tuTipoTaller = getTipoTallerDefecto();
			}
			else {
				tuTipoTaller = FactoriaDAO.recuperar(TipoSeccion.class, seleccionTipoTaller);
			}

			if (seleccionLocalidad == getLocalidadDefecto().getId()) {
				tuLocalidad = getLocalidadDefecto();
			}
			else {
				tuLocalidad = FactoriaDAO.recuperar(Poblacion.class, seleccionLocalidad);
			}
		}
		catch (SQLException e) {
			addActionError("Error al recuperar los datos seleccionados: " + e);
			res = ERROR;
		}

		if ((tuTipoTaller == null) || (tuLocalidad == null)) {
			addActionError("Error al cargar los datos seleccionados");
			res = ERROR;
		}

		try {
			// Crea un QueryBuilder 
			QueryBuilder<Taller, Integer> tallerQb = 
					FactoriaDAO.getDao(Taller.class).queryBuilder();
			Where<Taller, Integer> whereTallerQb = tallerQb.where();

			// Seleccionamos sólo talleres activos
			whereTallerQb.eq(Taller.ACTIVO_NAME, true);
			// Si localidad no es "Indiferente", seleccionamos por población
			if (!tuLocalidad.equals(getLocalidadDefecto())) {
				whereTallerQb.and().eq(Taller.ID_POBLACION_FIELD_NAME, tuLocalidad);
			}

			// Cogemos solo los que tengan precio por hora menor que el indicado
			if ((precioMaximo != null) && (!precioMaximo.isEmpty())) {
				whereTallerQb.and().le(Taller.COSTE_HORA_FIELD_NAME, new BigDecimal(precioMaximo).setScale(2));
			}

			// Si la especialidad no es indiferente, hay que filtrar
			if (!tuTipoTaller.equals(getTipoTallerDefecto())){
				QueryBuilder<Seccion, Integer> secQb = 
						FactoriaDAO.getDao(Seccion.class).queryBuilder();
				secQb.where().eq(Seccion.ID_TIPO_FIELD_NAME, 
						tuTipoTaller);

				tallerQb.join(secQb) // Hace un join 
				.distinct()    // SELECT DISTINCT
				.orderBy(Taller.NOMBRE_FIELD_NAME, true);
			}

			listadoTalleres = whereTallerQb.query();
		} catch (SQLException e) {
			addActionError("Error al buscar en la BD: " + e);
			res = ERROR;
		}				

		return res;
	}
	
	private String quote(String str) {
		return "\"" + str + "\"";
	}

	/**
	 * Obtiene la ocupación por secciones de un taller
	 * @return  actionerror
	 */
	@ProporcionaFuncionalidad(identificador = FactoriaFunciones.USUARIO_BUSCAR_TALLERES)	
	public String ocupacion() {
		ocupacionPorSecciones = new ArrayList<Object>();
		try {
			datosTallerSeleccionado = FactoriaDAO.recuperar(Taller.class, tallerSeleccionado);
			
			nombreSecciones = new HashMap<Integer, String>();
			
			for (TipoSeccion tipoSeccion: TipoSeccion.tiposSeccion()) {
				nombreSecciones.put(tipoSeccion.getId(), tipoSeccion.getNombre());
			}

			QueryBuilder<Seccion, Integer> seccionQb;
			seccionQb = FactoriaDAO.getDao(Seccion.class).queryBuilder();
			seccionQb.where().eq(Seccion.ID_TALLER_FIELD_NAME, tallerSeleccionado);
			
				QueryBuilder<Reparacion, Integer> reparacionQb =
						FactoriaDAO.getDao(Reparacion.class).queryBuilder();

				reparacionQb.where().eq(Reparacion.VISTOBUENO_FIELD_NAME, "false");

				reparacionQb.join(seccionQb)
							.selectRaw(quote(Reparacion.ID_SECCION_FIELD_NAME) + 
									   ", COUNT(*)")
							.groupBy(Reparacion.ID_SECCION_FIELD_NAME);
				GenericRawResults<OcupacionSeccion> consulta =
				FactoriaDAO.getDao(Reparacion.class).queryRaw(reparacionQb.prepareStatementString(),
						new RawRowMapper<OcupacionSeccion>() {
							public OcupacionSeccion mapRow(String[] columnNames, String[] resultColumns) {
								try {
									FactoriaDAO.recuperar(Seccion.class, Integer.valueOf(resultColumns[0]));
								} catch (NumberFormatException e) {
									addActionError(e.toString());
								} catch (SQLException e) {
									addActionError(e.toString());
								}
								return new OcupacionSeccion(nombreSecciones.get(Integer.valueOf(resultColumns[0])),
																	Integer.valueOf(resultColumns[1]));
							}
						});
				for (OcupacionSeccion res: consulta.getResults()) {
					ocupacionPorSecciones.add(res);
				}
		} catch (SQLException e) {
			addActionError(e.toString());
		}
		
		return SUCCESS;
	}
	
	/** 
	 * Función que valida un string para ver si es un BigDecimal
	 * @param t cadena
	 * @return  actionerror
	 */
	
	private boolean testBigDecimal(String t) {
		Pattern pattern;
		pattern = Pattern.compile("\\d+(?:\\.\\d{1,2})?|(?:\\d+)?\\.\\d{1,2}|\\d+(?:\\.)");
		
		if (pattern.matcher(t).matches())
			return true;
		else
			return false;
	}
}
